﻿#include <QtCore>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlRecord>
#include <utility>

#include "chat_message_define.h"
#include "chat_personal_define.h"

extern std::string xml2json(const char* xml_str);
namespace helper {
void  ReplaceEmojiMsgToImage(QString& source);
bool  DownloadWechatImage(const QString& src_path, QString& dst_path);
QSize GetImageSize(const QString& path);
int   GetDatabaseQueryCount(QSqlQuery& query);

QString FavoriteSubHistory2Html(const QList<QVariant>& msgList);
QString FavoriteHistory2Html(const QList<QVariant>& msgList);

QByteArray ReadDataFromFile(const QString& path);
bool       SaveContent2File(const QByteArray& data, const QString& path, bool enable_log);
bool       CopyFile2Target(const QString& source, const QString& target);
}  // namespace helper

namespace {
//全局数据
static QString kSourcePath   = TARGET_SOURCE_PATH;
static QString kResourcePath = TARGET_FAVORI_PATH;
static QString kOutputPath =
    QDir(TARGET_OUTPUT_PATH + QString("../favorite/")).absolutePath() + "/";
static QStringList kFileVec;

//过滤一些垃圾收藏或者涉及个人隐私的图片等
#ifdef _DEBUG
QSet<int> kSkipSet = {
    +138, +185, +296, +352, +386, +398, +421, +506, +579, +586, +622, +623, +624, +630, +667, +701,
    +728, +744, +814, +993, 1003, 1004, 1022, 1030, 1047, 1048, 1053, 1059, 1063, 1067, 1069, 1070,
    1071, 1076, 1081, 1083, 1093, 1094, 1095, 1096, 1098, 1115, 1118, 1120, 1125, 1126, 1127, 1128,
    1137, 1144, 1156, 1174, 1177, 1182, 1189, 1196, 1203, 1207, 1238, 1252, 1253, 1261, 1264, 1273,
    1275, 1309, 1310, 1320, 1325, 1326, 1330, 1332, 1333, 1337, 1344, 1345, 1364, 1367, 1373, 1375,
    1379, 1388, 1400, 1401, 1407, 1408, 1412, 1420, 1424, 1436, 1438, 1442, 1453, 1454, 1459, 1466,
    1467, 1485, 1487, 1491, 1498, 1500, 1501, 1502, 1503, 1504, 1505, 1512, 1519, 1522, 1523, 1524,
    1534, 1535, 1539, 1543, 1544, 1562, 1563, 1565, 1573, 1574, 1576, 1578, 1579, 1580, 1583, 1589,
    1592, 1594, 1603, 1604, 1606, 1621, 1628, 1629, 1678, 1680, 1691, 1702, 1703, 1710, 1711, 1724,
    1741, 1742, 1744, 1750, 1751, 1762, 1766, 1787, 1795, 1797, 1803, 1808, 1810, 1811, 1813, 1826,
    1827, 1831, 1833, 1839, 1840, 1846, 1852, 1853, 1856, 1864, 1870, 1887, 1889, 1896, 1922, 1897,
    1898, 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1907, 1911, 1923, 1926, 1932, 1935, 1936, 1946,
    1950, 1955, 1963, 1964, 1974, 1977, 1981, 1982, 1990, 1991, 1997, 1999, 2003, 2009, 2010, 2014,
    2018, 2024, 2032, 2037, 2044, 2048, 2055, 2057, 2066, 2073, 2074, 2076, 2078, 2084, 2086, 2089,
    2091, 2093, 2101, 2103, 2104, 2113, 2117, 2119, 2120, 2122, 2124, 2126, 2137, 2138, 2139, 2145,
    2146, 2154, 2173, 2185, 2187, 2221, 2271, 2277, 2292, 2302, 2307, 2310, 2326, 2333, 2355, 2362,
    2370, 2382, 2406, 2407, 2412, 2420, 2435, 2437, 2438, 2439, 2442, 2443, 2444, 2450, 2454, 2455,
    2456, 2461, 2463, 2465, 2466, 2470, 2473, 2492, 2501, 2502, 2504, 2507, 2510, 2511, 2515, 2521,
    2527, 2531, 2532, 2534, 2547, 2548, 2555, 2556, 2557, 2558, 2566, 2578, 2585, 2591, 2593, 2595,
    2598, 2599, 2602, 2605, 2608, 2610, 2612, 2613, 2614, 2619, 2620, 2624, 2630, 2633, 2638, 2641,
    2645, 2647, 2651, 2664, 2665, 2676, 2677, 2680, 2683, 2684, 2688, 2689, 2691, 2692, 2697, 2701,
    2703, 2709, 2745, 2757, 2780, 2787, 2788, 2790, 2795, 2849, 2852, 2858, 2864, 2870, 2872, 2888,
    2890, 2901, 2914, 2934, 2937, 2967, 2977, 2978, 2981, 2988, 2992, 2996, 3004, 3010, 3014, 3025,
    3035, 3047, 3059, 3075, 3076, 3077, 3078, 3098, 3099, 3100, 3101, 3107, 3116, 3119, 3120, 3121,
    3189, 3225, 3245, 3246, 3247, 3253, 3258, 3270, 3271, 3272, 3287, 3288, 3302, 3317, 3329, 3342,
    3343, 3400, 3403, 3404, 3405, 3413, 3422, 3423, 3426, 3430, 3432, 3439, 3440, 3442, 3443, 3444,
    3445, 3446, 3449, 3451, 3454, 3459, 3463, 3534, 3535, 3536, 3537, 3559, 3560, 3561, 3575, 3576,
    3577, 3584, 3585, 3601, 3602, 3603, 3622, 3623, 3624, 3635, 3636, 3637, 3642, 3670, 3683, 3688,
    3699, 3701, 3725, 3754, 3755, 3756, 3757, 3758, 3787, 3788, 3789, 3790, 3791, 3824, 3830, 3844,
    3857, 3858, 3859, 3860, 3861, 3862, 3881, 3883, 3898, 3902, 3906, 3910, 3947, 3948, 3949, 3950,
    3969, 3970, 3971, 3972, 4009, 4010, 4012, 4013, 4014, 4015, 4019, 4027, 4038, 4040, 4041, 4042,
    4073, 4074, 4096, 4130, 4198, 4199, 4202, 4205, 4206, 4207, 4227, 4231, 4241, 4249, 4254, 4255,
    4256, 4330, 4344, 4351, 4358, 4372, 4378, 4383, 4385, 4386, 4387, 4388, 4389, 4390, 4399, 4400,
    4419, 4441, 4450, 4456, 4459, 4461, 4462, 4467, 4468, 4474, 4477, 4478, 4495, 4503, 4504, 4509,
    4514, 4515, 4516, 4608, 4524, 4525, 4528, 4529, 4530, 4531, 4533, 4539, 4540, 4541, 4542, 4543,
    4544, 4554, 4555, 4558, 4610, 4611, 4656, 4658, 4659, 4661, 4666, 4675, 4700, 4707, 4708, 4709,
    4710, 4716, 4732, 4733, 4765, 4785, 4786, 4787, 4788, 4805, 4813, 4818, 4829, 4831, 4841, 4845,
    4846, 4862, 4891, 4907, 4916, 4982, 5001, 5002, 5042, 5043, 5162, 5179, 5182, 5201, 5207, 5260,
    5311, 5312, 5327, 5447, 5462, 5474, 5504, 5536, 5538, 5563, 5646, 5676, 5704, 5789, 5806, 5813,
};
QSet<int> kSkipSet2 = {
    1732, 1814, 1847, 1867, 1869, 1871, 1886, 1939, 1942, 1948, 1965, 1967, 1968, 1972, 1985, 2002,
    2008, 2016, 2019, 2031, 2035, 2036, 2045, 2046, 2054, 2056, 2058, 2067, 2068, 2069, 2071, 2072,
    2079, 2080, 2090, 2094, 2096, 2097, 2099, 2100, 2102, 2105, 2106, 2107, 2111, 2112, 2116, 2125,
    2141, 2142, 2155, 2156, 2184, 2198, 2201, 2207, 2209, 2214, 2219, 2236, 2261, 2269, 2283, 2290,
    2291, 2294, 2295, 2315, 2321, 2330, 2331, 2334, 2337, 2339, 2340, 2342, 2347, 2351, 2354, 2357,
    2363, 2369, 2380, 2381, 2383, 2385, 2394, 2402, 2421, 2425, 2431, 2434, 2448, 2453, 2457, 2467,
    2468, 2475, 2479, 2499, 2503, 2506, 2508, 2514, 2518, 2520, 2535, 2542, 2543, 2552, 2561, 2568,
    2570, 2573, 2574, 2579, 2580, 2588, 2589, 2594, 2596, 2597, 2600, 2601, 2603, 2604, 2607, 2628,
    2632, 2634, 2640, 2646, 2649, 2650, 2652, 2655, 2663, 2666, 2668, 2669, 2670, 2673, 2678, 2685,
    2686, 2687, 2698, 2699, 2700, 2702, 2707, 2708, 2710, 2711, 2713, 2715, 2717, 2719, 2731, 2734,
    2750, 2754, 2761, 2765, 2772, 2783, 2784, 2799, 2800, 2801, 2808, 2813, 2814, 2825, 2827, 2829,
    2832, 2833, 2834, 2850, 2854, 2863, 2877, 2887, 2896, 2902, 2906, 2912, 2924, 2926, 2930, 2931,
    2947, 2952, 2962, 2985, 2990, 2991, 2993, 2994, 3005, 3006, 3015, 3017, 3023, 3044, 3050, 3079,
    3085, 3088, 3112, 3163, 3170, 3171, 3175, 3182, 3183, 3188, 3248, 3250, 3277, 3278, 3283, 3324,
    3325, 3326, 3358, 3390, 3407, 3421, 3431, 3461, 3462, 3484, 3521, 3530, 3565, 3566, 3597, 3641,
    3643,
};
#else
QSet<int> kSkipSet;
QSet<int> kSkipSet2;
#endif

QStringList parseFromResourceDir(const QString& resourcePath) {
  QStringList   res;
  QDir          dir(resourcePath);
  QFileInfoList fileInfoList = dir.entryInfoList();
  foreach(const QFileInfo& fileInfo, fileInfoList) {
    if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
      continue;

    if (fileInfo.isFile()) {
      res.push_back(fileInfo.fileName());
    }
  }
  return res;
}

void parseResourcesFromDir() {
  kFileVec = parseFromResourceDir(kResourcePath + "res/");
}

QString findResourcesWithDataId(const QString& id, bool matchComplete) {
  auto target =
      std::find_if(kFileVec.begin(), kFileVec.end(), [id, matchComplete](const QString& item) {
        return matchComplete ? (item == id) : item.startsWith(id);
      });
  if (target == kFileVec.end()) {
    return QString();
  } else {
    return *target;
  }
}

QMap<QString, QVariant> parseFavitemFromXml(int localIndex, const QString& xmlBuff) {
  std::string     jsonNode = xml2json(xmlBuff.toStdString().c_str());
  QJsonParseError parseJsonErr;
  QJsonDocument   document =
      QJsonDocument::fromJson(QString::fromStdString(jsonNode).toUtf8(), &parseJsonErr);
  if (!(parseJsonErr.error == QJsonParseError::NoError)) {
    qWarning() << u8"解析第" << localIndex << u8"条数据出错，解析 json文件错误:"
               << parseJsonErr.errorString();
    return QMap<QString, QVariant>();
  }
  return document.object().toVariantMap()["favitem"].toMap();
}

QString getTimeFmt(qlonglong updateTime) {
  return QDateTime::fromSecsSinceEpoch(updateTime).toString("yyyy-MM-dd hh:mm:ss");
}

QString copyFavoriteImage(const QString& imageId,
                          const QString& prefix,
                          qlonglong      updateTime,
                          QString&       thumbImage) {
  //拷贝大图
  QString resourceBigImage = findResourcesWithDataId(imageId + ".", false);
  QString targetBigImage   = "images/" + FAV_BIG_IMAGE(imageId);
  QString targetOutImage   = kOutputPath + prefix + targetBigImage;
  if (QFile::exists(targetOutImage)) {
    // good
  } else if (!resourceBigImage.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + resourceBigImage, targetOutImage);
  } else {
    qWarning() << "image id:" << imageId
               << ", big image is not downloaded, time:" << getTimeFmt(updateTime);
  }

  //拷贝小图
  thumbImage               = findResourcesWithDataId(imageId + "_th.", false);
  QString targetSmallImage = "images/" + FAV_SMALL_IMAGE(imageId);
  targetOutImage           = kOutputPath + prefix + targetSmallImage;
  if (QFile::exists(targetOutImage)) {
    // good
    thumbImage = targetSmallImage;
  } else if (!thumbImage.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + thumbImage, targetOutImage);
    thumbImage = targetSmallImage;
  } else if (!resourceBigImage.isEmpty()) {
    helper::CopyFile2Target(kOutputPath + prefix + targetBigImage, targetOutImage);
    thumbImage = targetSmallImage;
  } else {
    qWarning() << "image id:" << imageId
               << ", small image is not downloaded, time:" << getTimeFmt(updateTime);
  }
  return targetBigImage;
}

QString copyFavoriteVideo(const QString& videoId,
                          const QString& prefix,
                          qlonglong      updateTime,
                          QString&       thumbImage) {
  // 拷贝视频
  QString resourceVideoPath = findResourcesWithDataId(videoId + ".", false);
  QString targetVideoPath   = "video/" + FAV_VIDEO_FILE(videoId);
  QString targetOutVideo    = kOutputPath + prefix + targetVideoPath;
  if (QFile::exists(targetOutVideo)) {
    // good
  } else if (!resourceVideoPath.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + resourceVideoPath, targetOutVideo);
  } else {
    qWarning() << "video id:" << videoId
               << ", video is not downloaded, time:" << getTimeFmt(updateTime);
  }

  // 拷贝小图
  thumbImage              = findResourcesWithDataId(videoId + "_th.", false);
  QString targetThumbPath = "video/" + FAV_VIDEO_THUMB(videoId);
  QString targetOutThumb  = kOutputPath + prefix + targetThumbPath;
  if (QFile::exists(targetOutThumb)) {
    // good
    thumbImage = targetThumbPath;
  } else if (!thumbImage.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + thumbImage, targetOutThumb);
    thumbImage = targetThumbPath;
  } else {
    qWarning() << "video id:" << videoId
               << ", thumbnail is not downloaded, time:" << getTimeFmt(updateTime);
  }
  return targetVideoPath;
}

void cleanFavoriteLink(QString& link) {
  // 去掉多余的符号
  link.replace("&from=timeline", "");
  link.replace("from=timeline&", "");
  link.replace("#wechat_redirect", "");
  link.replace("&isappinstalled=0", "");
  if (link.endsWith("#rd")) {
    link.chop(3);
  }
}

QString copyFavoriteLink(const QString& linkId,
                         const QString& link,
                         const QString& prefix,
                         QString        thumbUrl) {
  Q_ASSERT(!linkId.isEmpty());
  //再找缓存，下载，分析网页
  auto resourceThumb   = findResourcesWithDataId(linkId + "_th", false);
  auto targetLinkThumb = prefix + "link/" + FAV_LINK_THUMB(linkId);
  auto targetOutThumb  = kOutputPath + targetLinkThumb;
  if (QFile::exists(targetOutThumb)) {
    // good
  } else if (!resourceThumb.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + resourceThumb, targetOutThumb);
  } else if (!thumbUrl.isEmpty()) {
    QString tmp = targetOutThumb;
    helper::DownloadWechatImage(thumbUrl, tmp);
    if (tmp != targetOutThumb) {
      QFile::rename(tmp, targetOutThumb);
    }
  } else {
    // TODO 提取链接图标
    // if (link.contains("mp.weixin.qq.com")) {
    //  // TODO 下载微信网页并获得获得缩略图地址
    //  thumbUrl = "1234567";
    //} else if (link.contains("zhihu.com") || link.contains("douban.com") ||
    //           link.contains("weibo.cn") || link.contains("qq.com") || link.contains("jd.com") ||
    //           link.contains("cnblogs.com") || link.contains("163.com")) {
    //  // No thumbnail
    //  // 知乎应该是有缩略图的，到时候要验证下
    //  // qWarning() << "link thumbnail not exists, id:" << id << ", url:" << link;
    //  return QString();
    //}
    qWarning() << "link thumbnail not exists, id:" << linkId << ", url:" << link;
    targetLinkThumb.clear();
  }

  return targetLinkThumb;
}

QString copyFavoriteFile(const QString& fileId,
                         const QString& prefix,
                         const QString& format,
                         qlonglong      updateTime) {
  Q_ASSERT(!format.isEmpty());
  auto    resourceFile = findResourcesWithDataId(fileId + ".", false);
  QString targetFile   = "files/" + FAV_FILE_RESOURCE(fileId, format);
  QString outputFile   = kOutputPath + prefix + targetFile;
  if (QFile::exists(outputFile)) {
    // good
  } else if (!resourceFile.isEmpty()) {
    helper::CopyFile2Target(kResourcePath + "res/" + resourceFile, outputFile);
  } else {
    qWarning() << "file id:" << fileId
               << ", file is not downloaded, time:" << getTimeFmt(updateTime);
  }
  return targetFile;
}

QString saveFavChatHistoryHtml(const QString& title, const QString& content, const QString& path) {
  QString temporate = helper::ReadDataFromFile(kOutputPath + "chat/fav_sub_chat_tmp.html");
  QString bytes     = temporate.arg(title, content);
  helper::SaveContent2File(bytes.toUtf8(), path, true);
  return path;
}

QString copyFavoriteChatHistory(const QList<QVariant>& msgList) {
  for (const QVariant& data : msgList) {
    auto    dataItem   = data.toMap();
    auto    id         = dataItem["@dataid"].toString();
    auto    type       = dataItem["@datatype"].toInt();
    QString timeString = dataItem["datasrctime"].toString();
    bool    is_fmt     = timeString.length() == QString("yyyy-MM-dd hh:mm:ss").length();
    qint64  time =
        QDateTime::fromString(timeString, is_fmt ? "yyyy-MM-dd hh:mm:ss" : "yyyy-M-d hh:mm")
            .toSecsSinceEpoch();

    if (type == FavType_Text) {
      // no need to copy
    } else if (type == FavType_Image) {
      QString thumbImage;
      copyFavoriteImage(id, "chat/", time, thumbImage);
    } else if (type == FavType_Video) {
      QString thumbImage;
      copyFavoriteVideo(id, "chat/", time, thumbImage);
    } else if (type == FavType_Link) {
      auto link = dataItem["dataitemsource"].toMap()["link"].toString();
      if (link.isEmpty()) {
        link = dataItem["weburlitem"].toMap()["clean_url"].toString();
      }
      copyFavoriteLink(id, link, "chat/", QString());
    } else if (type == FavType_Location) {
      // no need to copy
    } else if (type == FavType_File) {
      auto fmt = dataItem["datafmt"].toString();
      copyFavoriteFile(id, "chat/", fmt, time);
    } else if (type == FavType_InnerChatHistory) {
      auto title      = dataItem["datatitle"].toString();
      auto subHistory = dataItem["recordxml"]
                            .toMap()["recordinfo"]
                            .toMap()["datalist"]
                            .toMap()["dataitem"]
                            .toList();
      auto subHtml = helper::FavoriteSubHistory2Html(subHistory);
      auto path    = kOutputPath + "chat/" + FAV_SUB_HISTORY_HTML(id);
      saveFavChatHistoryHtml(title, subHtml, path);
    } else if (type == FavType_MiniProgram) {
      auto    thumb  = dataItem["appbranditem"].toMap()["iconurl"].toString();
      QString target = kOutputPath + "chat/link/" + FAV_LINK_THUMB(id);
      if (thumb.isEmpty()) {
        qWarning() << "copy mini program thumb fail, no url, id:" << id;
      } else if (!QFile::exists(target)) {
        QString tmp = target;
        helper::DownloadWechatImage(thumb, tmp);
        if (tmp != target) {
          QFile::rename(tmp, target);
        }
      }
    } else {
      qDebug() << "type:" << type << " unsupport";
    }
  }
  //转为html，相当于笔记类型了。是前面类型的集合，以dataitem为区分
  //要想办法根据username找到昵称(备注)和头像
  auto html = helper::FavoriteHistory2Html(msgList);
  return html;
}
}  // namespace

namespace wechat {
//从数据库里面直接读取头像数据 Misc.db，ffd8是jpg，8950是png
void DecodeFavoriteMessage() {
  QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
  db.setDatabaseName(kSourcePath + R"(Msg\dec_Favorite.db)");
  if (!db.open()) {
    qWarning() << "open database failed";
    return;
  }

  //查询微信收藏记录
  QSqlQuery query(db);
  query.prepare("SELECT * FROM FavItems ORDER BY FavLocalID");
  query.exec();

  //统计数目
  int result_count = helper::GetDatabaseQueryCount(query);
  qDebug() << "find " << result_count << " messages";

  // 先找出本地目录的map，用于快速查询
  parseResourcesFromDir();

  QJsonArray jsonArray;
  while (query.next()) {
    //过滤一些垃圾收藏或者涉及个人隐私的图片等
    auto localIndex = query.value("FavLocalID").toInt();
    if (kSkipSet.contains(localIndex) || kSkipSet2.contains(localIndex)) {
      continue;
    }

    auto type       = query.value("Type").toInt();
    auto updateTime = query.value("UpdateTime").toLongLong();
    auto xmlBuff    = query.value("XmlBuf").toString();

    //解析xml数据
    auto&& jsonMap = parseFavitemFromXml(localIndex, xmlBuff);
    if (jsonMap.isEmpty()) {
      continue;
    }
    auto desc      = jsonMap["desc"].toString();
    auto title     = jsonMap.contains("title") ? jsonMap["title"].toString() : desc;
    auto source    = jsonMap["source"].toMap();
    auto dataItem  = jsonMap["datalist"].toMap()["dataitem"].toMap();
    auto dataId    = dataItem["@dataid"].toString();
    auto searchKey = query.value("SearchKey").toString();
    if (searchKey.endsWith('\n')) {
      searchKey.chop(1);
    }

    //构造json格式的数据结构
    QJsonObject item;
    item["m_uiMesLocalID"]  = localIndex;
    item["m_uiMessageType"] = type;
    item["m_nsContent"]     = desc;

    if (type == FavType_Text) {
      helper::ReplaceEmojiMsgToImage(desc);
      item["m_nsContent"] = desc;

    } else if (type == FavType_Image) {
      QString prefix = "normal/";
      QString thumbPath;
      QString imagePath = copyFavoriteImage(dataId, prefix, updateTime, thumbPath);
      if (imagePath.isEmpty()) {
        item["m_nsOriginal"] = xmlBuff;
      }
      item["m_nsContent"]   = imagePath.isEmpty() ? imagePath : prefix + imagePath;
      item["m_nsThumbnail"] = thumbPath.isEmpty() ? thumbPath : prefix + thumbPath;
      if (!imagePath.isEmpty()) {
        auto&& size        = helper::GetImageSize(kOutputPath + prefix + imagePath);
        item["m_nsWidth"]  = size.width();
        item["m_nsHeight"] = size.height();
      }

    } else if (type == FavType_Video ||
               (type == FavType_Extend && dataItem["@datatype"].toInt() == FavType_Video2)) {
      QString prefix = "normal/";
      QString thumbPath;
      QString videoPath = copyFavoriteVideo(dataId, prefix, updateTime, thumbPath);
      if (videoPath.isEmpty()) {
        item["m_nsOriginal"] = xmlBuff;
      }
      item["m_uiMessageType"] = FavType_Video;
      item["m_nsContent"]     = videoPath.isEmpty() ? videoPath : prefix + videoPath;
      item["m_nsThumbnail"]   = thumbPath.isEmpty() ? thumbPath : prefix + thumbPath;

    } else if (type == FavType_Link) {
      auto link = source["link"].toString();
      title     = dataItem["datatitle"].toString();
      desc      = dataItem["datadesc"].toString();
      if (dataId.isEmpty()) {
        dataId = source["@sourceid"].toString();
      }

      QString thumbUrl;
      if (jsonMap.contains("weburlitem")) {
        auto dataItem = jsonMap["weburlitem"].toMap();
        if (title.isEmpty()) {
          title = dataItem["pagetitle"].toString();
        }
        if (desc.isEmpty()) {
          desc = dataItem["pagedesc"].toString();
        }
        thumbUrl = dataItem["pagethumb_url"].toString();
      }

      cleanFavoriteLink(link);
      item["m_nsTitle"]       = title.isEmpty() ? link : title;
      item["m_nsContent"]     = link;
      item["m_nsDescription"] = desc;
      item["m_nsThumbnail"]   = copyFavoriteLink(dataId, link, "normal/", thumbUrl);

    } else if (type == FavType_File) {
      auto format = dataItem["datafmt"].toString();
      //处理文件名
      title = title.isEmpty() ? dataItem["datatitle"].toString() : title;
      if (!title.isEmpty() && !title.contains(".")) {
        title += QString(".").append(format);
      }
      QString filePath = copyFavoriteFile(dataId, "normal/", format, updateTime);
      if (filePath.isEmpty()) {
        item["m_nsOriginal"] = xmlBuff;
      } else {
        filePath = "normal/" + filePath;
      }
      item["m_nsFormat"]    = format;
      item["m_nsTitle"]     = title;
      item["m_nsContent"]   = filePath;
      item["m_nsFileExist"] = !filePath.isEmpty();
      item["m_nsFileSize"]  = dataItem["fullsize"].toLongLong();

    } else if (type == FavType_ChatHistory) {
      //拷贝资源并转换为html
      auto msgList = jsonMap["datalist"].toMap()["dataitem"].toList();
      auto historyId =
          QString::number(localIndex) + "_" + msgList.at(0).toMap()["@dataid"].toString();
      auto path = kOutputPath + "chat/" + FAV_CHAT_HISTORY_HTML(historyId);
      //如果本地处理过则无须重复处理
      if (!QFile::exists(path)) {
        QString html = copyFavoriteChatHistory(msgList);
        if (title.isEmpty()) {
          title = source["fromusr"].toString();
        }
        saveFavChatHistoryHtml(title, html, path);
      }
      //处理emoji表情
      if (desc.isEmpty()) {
        desc = searchKey;
      }
      // TODO 273, desc is empty, generate?
      helper::ReplaceEmojiMsgToImage(desc);
      item["m_nsTitle"]       = title;
      item["m_nsContent"]     = path.mid(kOutputPath.length());
      item["m_nsDescription"] = desc;
      item["m_nsOriginal"]    = xmlBuff;

    } else if (type == FavType_Extend) {
      qWarning() << u8"其他扩展信息";
    } else if (type == FavType_WeChatNote) {
      //笔记，TODO，这个也有可能需要自己生成简介
      auto noteList = jsonMap["datalist"].toMap()["dataitem"].toList();
      if (desc.isEmpty()) {
        desc = searchKey;
      }
      item["m_nsTitle"]       = title;
      item["m_nsDescription"] = desc;
      item["m_nsOriginal"]    = xmlBuff;
      qWarning() << u8"笔记:要用模板拼接";

    } else if (type == FavType_MiniProgram) {
      if (title.isEmpty()) {
        title = dataItem["datatitle"].toString();
        desc  = dataItem["datadesc"].toString();
      }
      auto appItem        = jsonMap["appbranditem"].toMap();
      auto appId          = appItem["appid"].toString();
      auto appBrandId     = source["brandid"].toString();
      auto appIcon        = appItem["iconurl"].toString();
      auto appDisplayName = appItem["sourcedisplayname"].toString();
      // qDebug() << "id: " << appId << ", title:"<< title << ", brand:" << appBrandId <<", name:"
      // << appDisplayName;

    } else if (type == FavType_ShiPinHao) {
      //视频号收藏，只能自己补一些截图和描述了
      auto feedItem     = jsonMap["finderFeed"].toMap();
      auto feedNickName = feedItem["nickname"].toString();  //分享者
      auto feedUserName = feedItem["username"].toString();
      auto feedAvatar   = feedItem["avatar"].toMap()["#text"].toString();
      auto thumb =
          feedItem["mediaList"].toMap()["media"].toMap()["thumbUrl"].toMap()["#text"].toString();
      desc = feedItem["desc"].toString();
      // TODO 有缩略图，可以解析下来存储
      // qDebug() << u8"视频号:" << desc;
    } else {
      item["m_nsContent"] = xmlBuff;
      qWarning() << "unsupport favorite type:" << type;
    }

    //更新时间等
    item["m_nsSearchKey"]  = searchKey;
    item["m_uiCreateTime"] = updateTime;

    // TODO 处理消息来源
    auto fromUser            = query.value("FromUser").toString();
    auto realUser            = query.value("RealChatName").toString();
    item["m_nsFromUsr"]      = fromUser;
    item["m_nsToUsr"]        = TARGET_USER;
    item["m_nsRealChatName"] = realUser;

    jsonArray.push_back(item);
  }

  //格式化json
  QString content = QJsonDocument(jsonArray).toJson(QJsonDocument::Indented);

  //读取模板信息并写入到新的文件
  QString bytes = helper::ReadDataFromFile(kOutputPath + "js/message_wechat_tmp.js");
  helper::SaveContent2File(bytes.arg(content).toUtf8(), kOutputPath + "js/message_fav.js", true);
}
}  // namespace wechat
